package com.lagou.edu.utils;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Component;
import com.lagou.edu.annotation.Qualifier;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.factory.ProxyFactory;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zx
 * @date 2021/6/30 20:48
 * @description 用于解析 文件并返回bean
 */
public class AnnotationClassPathXmlApplicationContext {

    //存储扫描到的包路径下所以的class类
    private static List<Class<?>> classes = new ArrayList<>();

    // 包含事务控制的对象名
    private static List<String> transactionalClassNames = new ArrayList<>();

    // 包含事务控制的id
    private static List<String> transactionalIds = new ArrayList<>();

    // 受容器控制的beanMap
    private static Map<String, Object> beanMap = new HashMap<>();

    public void init(String packageName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 按路径扫描所有类并且记录
        doScanner(packageName);

        // 初始化受容器管理的bean
        initBeans(classes);

        // 注入bean
        doDI();

        // 处理AOP
        doAop();
    }

    private void doAop() {
        for (String id : transactionalIds) {
            ProxyFactory proxyFactory = (ProxyFactory) beanMap.get("proxyFactory");
            Object instance = beanMap.get(id);
            Class<?> aClass = instance.getClass();
            Class<?>[] interfaces = aClass.getInterfaces();
            // 判断执行不同动态代理
            if (interfaces != null && interfaces.length > 0) {
                instance = proxyFactory.getJdkProxy(instance);
            } else {
                instance = proxyFactory.getCglibProxy(instance);
            }
            beanMap.put(id, instance);
        }
    }

    /**
     * 实现依赖注入所有
     */
    private void doDI() throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            //  注入属性
            Class<?> clazz = entry.getValue().getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //Autowire默认使用byType的方式装配
                Autowired autoAnnotation = field.getAnnotation(Autowired.class);
                field.setAccessible(true);
                if (autoAnnotation != null) {
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    if (qualifier != null && !StringUtils.isEmpty(qualifier.value())) {
                        //按照名字注入
                        field.set(entry.getValue(), beanMap.get(qualifier.value()));
                        continue;
                    }   //按照类型注入
                    field.set(entry.getValue(), beanMap.get(field.getType().getName()));
                }
            }
        }
    }


    /**
     * 1.判断注解 通过注解将受管理的类储存起来
     * 2.受事务管控的类 生成代理类
     */
    private void initBeans(List<Class<?>> classes) throws IllegalAccessException, InstantiationException {

        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(Service.class)) {
                Service service = clazz.getAnnotation(Service.class);
                String beanName = service.value();
                if (StringUtils.isEmpty(beanName)) {
                    // 首字母小写
                    beanName = StringUtils.uncapitalize(clazz.getSimpleName());
                }
                Class<?>[] interfaces = clazz.getInterfaces();
                // 包含代表需要生成动态代理类
                Object instance = clazz.newInstance();
                // 注入
                beanMap.put(beanName, instance);
                //按照类型存储一个实例关系(为了方便按照类型注入)
                beanMap.put(clazz.getName(), instance);
                // 记录需要动态代理的类
                if (transactionalClassNames.contains(clazz.getName())) {
                    transactionalIds.add(beanName);
                    transactionalIds.add(clazz.getName());
                }
                // 按接口类型再储存一个
                for (Class<?> i : interfaces) {
                    beanMap.put(i.getName(), instance);
                    // 记录需要动态代理的类
                    if (transactionalClassNames.contains(clazz.getName())) {
                        transactionalIds.add(i.getName());
                    }
                }
            } else if (clazz.isAnnotationPresent(Component.class)) {
                Component component = clazz.getAnnotation(Component.class);
                String beanName = component.name();
                if (StringUtils.isEmpty(beanName)) {
                    // 首字母小写
                    beanName = StringUtils.uncapitalize(clazz.getSimpleName());
                }
                beanMap.put(beanName, clazz.newInstance());
                // 按类型
                beanMap.put(clazz.getName(), clazz.newInstance());
            } else if (clazz.isAnnotationPresent(Repository.class)) {
                Repository repository = clazz.getAnnotation(Repository.class);
                String beanName = repository.value();
                if (StringUtils.isEmpty(beanName)) {
                    // 首字母小写
                    beanName = StringUtils.uncapitalize(clazz.getSimpleName());
                }
                beanMap.put(beanName, clazz.newInstance());
                // 按类型
                beanMap.put(clazz.getName(), clazz.newInstance());
                Class<?>[] interfaces = clazz.getInterfaces();
                for (Class<?> i : interfaces) {
                    beanMap.put(i.getName(), clazz.newInstance());
                }
            }
        }
    }

    /**
     * 根据包名扫描包下所有的文件
     */
    public void doScanner(String packageName) throws ClassNotFoundException, IllegalArgumentException {
        //扫描包下面的类
        String path = packageName.replace(".", "/");
        //项目路径
        URL url = this.getClass().getClassLoader().getResource(path);
        //判断路径是否存在
        if (url != null) {
            File dir = new File(url.getPath());
            for (File file : dir.listFiles()) {
                //判断是否是目录 递归调用
                if (file.isDirectory()) {
                    doScanner(packageName + "." + file.getName());
                } else {
                    //获取class文件路径和文件名
                    String className = packageName + "." + file.getName().replace(".class", "").trim();
                    Class<?> classFile = Class.forName(className);
                    // 记录关系
                    classes.add(classFile);
                    // 记录包含事务控制的注解类
                    Method[] declaredMethods = classFile.getDeclaredMethods();
                    for (Method declaredMethod : declaredMethods) {
                        Transactional transactional = declaredMethod.getAnnotation(Transactional.class);
                        if (transactional != null) {
                            transactionalClassNames.add(classFile.getName());
                        }
                    }
                }
            }
        }
    }

    public static Object getBean(String id) {
        return beanMap.get(id);
    }
}
